package com.liang.pcc.service.impl;

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liang.pcc.common.exception.ClientException;
import com.liang.pcc.domain.AuthUser;
import com.liang.pcc.dto.req.UserLoginReq;
import com.liang.pcc.dto.req.UserRegisterReq;
import com.liang.pcc.dto.req.UserUpdateReq;
import com.liang.pcc.dto.resp.UserLoginResp;
import com.liang.pcc.mapper.AuthUserMapper;
import com.liang.pcc.service.AuthUserService;
import com.liang.pcc.util.PasswordUtil;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Objects;

import static com.liang.pcc.common.constant.UserKeyConstants.USER_REGISTER_LOCK;
import static com.liang.pcc.common.errorcode.UserErrorCode.*;

/**
 * 系统用户表Service实现
 */
@Service
@RequiredArgsConstructor
public class AuthUserServiceImpl extends ServiceImpl<AuthUserMapper, AuthUser> implements AuthUserService {

    private final RedissonClient redissonClient;
    private final RBloomFilter<String> accountBloomFilter;

    @Override
    public void register(UserRegisterReq param) {
        if (accountBloomFilter.contains(param.getAccount())) {
            throw new ClientException(ACCOUNT_EXIST);
        }
        RLock lock = redissonClient.getLock(USER_REGISTER_LOCK + param.getAccount());
        if (!lock.tryLock()) {
            throw new ClientException(ACCOUNT_EXIST);
        }
        try {
            AuthUser user = BeanUtil.copyProperties(param, AuthUser.class);
            user.setPassword(PasswordUtil.getAESEncryption(param.getPassword()));
            save(user);
            accountBloomFilter.add(param.getAccount());
        } finally {
            lock.unlock();
        }
    }

    @Override
    public UserLoginResp login(UserLoginReq param) {
        // 账号不存在，则先注册
        if (!accountBloomFilter.contains(param.getAccount())) {
            throw new ClientException(ACCOUNT_NOT_EXIST);
        }
        AuthUser user = lambdaQuery()
                .eq(AuthUser::getAccount, param.getAccount())
                .eq(AuthUser::getPassword, PasswordUtil.getAESEncryption(param.getPassword()))
                .one();
        if (user == null) {
            throw new ClientException(PASSWORD_ERROR);
        }
        // 如果当前时间大于解封时间，则解封
        if (user.getUnsealingTime() != null && user.getUnsealingTime().isBefore(LocalDateTime.now())) {
            user.setEnableStatus(0);
            user.setUnsealingTime(null);
            updateById(user);
        }
        if (user.getEnableStatus() != 0) {
            throw new ClientException(String.format("账号已封禁! 解封时间：%s", user.getUnsealingTime()));
        }
        StpUtil.login(param.getAccount());
        return UserLoginResp.builder()
                .account(user.getAccount())
                .username(user.getUsername())
                .avatar(user.getAvatar())
                .phone(user.getPhone())
                .email(user.getEmail())
                .saTokenInfo(StpUtil.getTokenInfo())
                .build();
    }

    @Override
    public void logout(String account) {
        if (!accountBloomFilter.contains(account)) {
            throw new ClientException(USER_NOT_EXIST);
        }
        if (!StpUtil.isLogin(account)) {
            throw new ClientException(USER_NOT_LOGIN);
        }
        StpUtil.logout(account);
    }

    @Override
    public void updateUserInfo(UserUpdateReq param) {
        lambdaUpdate()
                .eq(Objects.equals(param.getAccount(), StpUtil.getLoginId()), AuthUser::getAccount, param.getAccount())
                .update(BeanUtil.toBean(param, AuthUser.class));
    }

    @Override
    public void updatePassword(UserUpdateReq param) {
        // 新密码不能和旧密码相同
        if (Objects.equals(param.getOriginalPassword(), param.getPassword())) {
            throw new ClientException(PASSWORD_SAME_ERROR);
        }
        lambdaQuery()
                .eq(Objects.equals(param.getAccount(), StpUtil.getLoginId()), AuthUser::getAccount, param.getAccount())
                .eq(AuthUser::getPassword, PasswordUtil.getAESEncryption(param.getOriginalPassword()))
                .oneOpt()
                .orElseThrow(() -> new ClientException(ORIGIN_PASSWORD_ERROR));
        // 确保修改用户为当前登录用户
        lambdaUpdate()
                .eq(Objects.equals(param.getAccount(), StpUtil.getLoginId()), AuthUser::getAccount, param.getAccount())
                .update(AuthUser.builder()
                        .account(StpUtil.getLoginId().toString())
                        .password(PasswordUtil.getAESEncryption(param.getPassword()))
                        .build());
    }

    @Override
    public Long getUserIdByAccount(String account) {
        return lambdaQuery()
                .eq(AuthUser::getAccount, StpUtil.getLoginId().toString())
                .one()
                .getId();
    }
}